package smartrpc.boot.starter.component;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import smart.rpc.annotation.SmartRPCAutowired;
import smart.rpc.client.ClientContainer;
import smart.rpc.exception.SmartRPCException;
import smart.rpc.factory.SmartRPCBeanFactory;
import smartrpc.boot.starter.config.SmartRPCProperties;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;

/**
 * Created By gao_e on 2020/3/8 12:30
 * 注入客户端用于 远程调用 的代理对象到 controller 或 service中
 */
@Component
@EnableConfigurationProperties({SmartRPCProperties.class})
@ConditionalOnProperty(
        prefix = "rpcserver",
        name = "needRequestOtherRPCServer",
        havingValue = "true"
)
public class SmartRPCClientInit implements InitializingBean {
    @Autowired
    private SmartRPCProperties smartRPCProperties;
    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void log() {
        System.out.println("已经加载完Smart RPC Client端相关自动配置");
        System.out.println("SmartRPCProperties = " + smartRPCProperties);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("===== 开始将rpc代理对象注入到spring ioc 中的 server、controller bean 属性中 =====");
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(Service.class);
        Map<String, Object> beansWithAnnotation1 = applicationContext.getBeansWithAnnotation(Controller.class);
        beansWithAnnotation.putAll(beansWithAnnotation1);
        System.out.println("===== SmartRPCAutowiredOPT obj bean size = " + beansWithAnnotation.size() + " =====");
        int optedNum = this.smartRPCAutowiredOPT(beansWithAnnotation);
        System.out.println("将客户端的用于RPC远程调用的动态代理对象注入到IOC中的controller或service对象中的操作次数为 = OptedNum = " + optedNum);
        // 将注册地址从spring配置文件对应参数类同步到RPC客户端容器中,便于RPC客户端在 进行请求时触发 加载获取 注册中心服务提供者 的信息
        ClientContainer.registHost = smartRPCProperties.getRegistHost();
    }

    // 动态注入
    public int smartRPCAutowiredOPT(Map<String, Object> beanMap) {
        int optNum = 0;
        Set<Map.Entry<String, Object>> entries = beanMap.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            // 遍历获取每一个 controller或service对象
            Object serviceOrControllerBean = entry.getValue();
            // 获取bean类信息对象Class
            Class<?> serviceOrControllerBeanClass = serviceOrControllerBean.getClass();
            // 判断操作
            boolean opt = this.opt(serviceOrControllerBean, serviceOrControllerBeanClass);
            if(opt)
                optNum++;
        }
        return optNum;
    }

    public boolean opt(Object serviceOrControllerBean, Class<?> serviceOrControllerBeanClass) {
        boolean opted = false;
        Field[] declaredFields = serviceOrControllerBeanClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if(!declaredField.isAnnotationPresent(SmartRPCAutowired.class))
                continue;
            // 寻找带有@SmartRPCAutowired注解的属性
            // 操作它
            Class<?> type = declaredField.getType();
            if(!type.isInterface())
                throw new SmartRPCException("@SmartRPCAutowired need injected interface , There is error "
                        + serviceOrControllerBean.getClass().getSimpleName() + " , field is "
                        + declaredField.getName());
            // 获取代理对象
            Object beanInstance = SmartRPCBeanFactory.getBeanByFacadeCls(type);
            //TODO 将beanInstance放置到IOC容器中，寻求方法中
            // 暴力靠近
            declaredField.setAccessible(true);
            try {
                // 反射调用执行declaredField的set方法
                declaredField.set(serviceOrControllerBean, beanInstance);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                throw new SmartRPCException("There is error "
                        + serviceOrControllerBean.getClass().getSimpleName() + " , field is "
                        + declaredField.getName()
                        + " reflex invoke the SetMethod error " + e);
            }
            System.out.println("===== Reflex invoke the SetMethod success , "
                    + serviceOrControllerBean.getClass().getSimpleName() + " , field is "
                    + declaredField.getName() + " =====");
            opted = true;
        }
        return opted;
    }

}
